/**
 * 
 */
package persistence;

import java.io.IOException;
import java.util.Vector;

import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;

import javax.microedition.rms.RecordStoreNotOpenException;
import serialization.Serializable;
import serialization.SerializationUtils;
import exception.ApplicationException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.util.Enumeration;
import java.util.Hashtable;

/**
 * Creation date: Nov 12, 2007
 * Use case: 
 *
 * @author: cristian.rasch
 */
public class ExtendedRecordStore {

    private RecordStore recordStore;

    public ExtendedRecordStore(String name) throws ApplicationException {
        try {
            recordStore = RecordStore.openRecordStore(name, true, RecordStore.AUTHMODE_PRIVATE, false);
        }
        catch (RecordStoreException e) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, e.getMessage());
        }
    }

    public void close() throws ApplicationException {
        try {
            recordStore.closeRecordStore();
        }
        catch (RecordStoreException e) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, e.getMessage());
        }
    }

    private void addRecord(byte[] data) throws ApplicationException {
        try {
            recordStore.addRecord(data, 0, data.length);
        }
        catch (RecordStoreException e) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, e.getMessage());
        }
    }

    public void save(Serializable[] objects) throws ApplicationException {
        try {
            for (int i = 0; i < objects.length; i++) {
                byte[] data = SerializationUtils.writeObject(objects[i]);
                addRecord(data);
            }
        }
        catch (IOException e) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, e.getMessage());
        }
    }

    public void saveObjectArray(Serializable[][] objects) throws ApplicationException {
        for (int i = 0; i < objects.length; i++) {
            try {
                byte[] data = SerializationUtils.writeObjectArray(objects[i]);
                addRecord(data);
            }
            catch (IOException e) {
                throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, e.getMessage());
            }
        }
    }
    // map should be of the form of Hashtable<String, String>
    public void saveMap(Hashtable map) throws ApplicationException {
        Enumeration keys = map.keys();
        Enumeration values = map.elements();

        String key, value;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            while(keys.hasMoreElements()) {
                baos.reset();
                key = keys.nextElement().toString();
                value = values.nextElement().toString();
                baos.write(key.length());
                baos.write(key.getBytes());
                baos.write(value.length());
                baos.write(value.getBytes());
                addRecord(baos.toByteArray());
            }
        } catch(IOException ioe) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, ioe.getMessage());
        } 
    }

    public Serializable[] loadAll(Class clazz) throws ApplicationException {
        try {
            RecordEnumeration enumeration = recordStore.enumerateRecords(null, null, false);
            Serializable serializable;
            Vector objects = new Vector();

            while (enumeration.hasNextElement()) {
                try {
                    serializable = (Serializable) clazz.newInstance();
                }
                catch (InstantiationException e) {
                    throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, e.getMessage());
                }
                catch (IllegalAccessException e) {
                    throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, e.getMessage());
                }
                serializable = SerializationUtils.readObject(enumeration.nextRecord(), clazz);
                objects.addElement(serializable);
            }
            Serializable[] serializables = new Serializable[objects.size()];
            objects.copyInto(serializables);

            return serializables;
        }
        catch (IOException e) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, e.getMessage());
        }
        catch (RecordStoreException e) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, e.getMessage());
        }
    }
    
    public Serializable[][] loadAllObjectArray(Class clazz) throws ApplicationException {
        try {
            RecordEnumeration enumeration = recordStore.enumerateRecords(null, null, false);
            Vector objects = new Vector();

            while (enumeration.hasNextElement()) {
                Serializable[] serializables = SerializationUtils.readObjectArray(enumeration.nextRecord(), clazz);
                objects.addElement(serializables);
            }
            Serializable[][] serializables = new Serializable[objects.size()][];
            for(int i = 0; i< serializables.length;  i++)
                serializables[i] = (Serializable[]) objects.elementAt(i);

            return serializables;
        }
        catch (IOException e) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, e.getMessage());
        }
        catch (RecordStoreException e) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, e.getMessage());
        }
    }

    public Hashtable loadMap() throws ApplicationException {
        Hashtable map = new Hashtable();
        byte keyLength, valueLength;
        String key, value;
        ByteArrayInputStream bais;

        try {
            RecordEnumeration records = recordStore.enumerateRecords(null, null, false);
            while(records.hasNextElement()) {
                bais = new ByteArrayInputStream(records.nextRecord());
                DataInputStream dis = new DataInputStream(bais);
                keyLength = dis.readByte();
                key = SerializationUtils.pullString(dis, keyLength);
                valueLength = dis.readByte();
                value = SerializationUtils.pullString(dis, valueLength);
                map.put(key, value);
            }
        } catch (RecordStoreException rse) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, rse.getMessage());
        } catch (IOException ioe) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, ioe.getMessage());
        }

        return map;
    }

    public void clearData() throws ApplicationException {
        try {
            RecordEnumeration e = recordStore.enumerateRecords(null, null, true);
            while(e.hasNextElement())
                recordStore.deleteRecord(e.nextRecordId());
        } catch (RecordStoreException ex) {
            throw new ApplicationException(ApplicationException.RMS_PERSISTENCE_EXCEPTION, ex.getMessage());
        }
    }

}
